home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Nebula 1
/
Nebula One.iso
/
Communications
/
GatorGeo
/
Source
/
GeoClient.m
< prev
next >
Wrap
Text File
|
1995-06-12
|
15KB
|
464 lines
// GeoClient.m
// By Charles G. Fleming, Educational Computing Services, Allegheny College.
// You may freely copy, distribute and reuse this code.
// Allegheny College and the author disclaim any warranty of any kind,
// expressed or implied, as to its fitness for any particular use.
// This work was partially supported by a grant from the Vira Heinz Endowment.
#import "GeoClient.h"
#import "Subprocess.h"
#import <appkit/Panel.h>
#import <appkit/Application.h>
#import <appkit/TextField.h>
#import <appkit/PopUpList.h>
#import <appkit/MenuCell.h>
#import <appkit/ScrollView.h>
#import <appkit/Text.h>
#import <appkit/Button.h>
#import <objc/Storage.h>
#import <objc/Object.h>
#import <strings.h>
#import <string.h>
#import <stdlib.h>
@implementation GeoClient
int stuffer(struct dataRecord* , char *);
- appDidInit:sender
{
subprocess = [[Subprocess allocFromZone:[self zone]] init:"/bin/csh" withDelegate:self andPtySupport:YES andStdErr:YES];
[subprocess send:"telnet martini.eecs.umich.edu 3000"];
records = [[Storage allocFromZone:[self zone]] initCount:10
elementSize:sizeof(struct dataRecord)
description:"{[50c][50c][50c][50c][50c][50c][50c][50c][50c][50c][50c][50c][50c][50c][50c][50c][50c]}"];
// Set up the pull down lists.
statesPullDownList = [statesPullDownListButton target];
[statesPullDownList setTarget:self];
[statesPullDownList setAction:@selector(selectState:)];
moreStatesPullDownList = [moreStatesPullDownListButton target];
[moreStatesPullDownList setTarget:self];
[moreStatesPullDownList setAction:@selector(selectState:)];
// Display a message until login is complete.
[NXApp runModalFor:loginMessageWindow];
return self;
}
// Query the database for information on a particular city.
- query:sender
{
char *cityAndState;
// Enable the button only after the query is finished.
[sender setEnabled:NO];
// So that we will receive text did change messages from our text fields.
[self makeFirstResponder:self];
// Reset the pull down list, clear the data records in the storage object and clear the
// text fields.
[self resetPullDownList];
[records empty];
[self clearTextFields];
cityAndState = malloc(strlen([cityTextField stringValue])+strlen([stateTextField stringValue])+3);
// Build the query string for the database server. The query string will be "City, State" or "City".
if(strlen([stateTextField stringValue]) > 0)
sprintf(cityAndState, "%s, %s", [cityTextField stringValue],[stateTextField stringValue]);
else
strcpy(cityAndState, [cityTextField stringValue]);
// Send the query to the subprocess object.
[subprocess send:cityAndState];
// Searching message window.
[searchingMessageWindow makeKeyAndOrderFront:self];
free(cityAndState);
return self;
}
// Put the information for the state selected from the pull down list into the text fields.
- selectState:sender
{
int tag;
struct dataRecord *newRecord;
// Get the record for the selected state.
tag = [[sender selectedCell] tag];
newRecord = (struct dataRecord *)[records elementAt:tag];
// Put the information into the text fields.
[stateTextField setStringValue:newRecord->stateName];
[countyCodeTextField setStringValue:newRecord->countyCode];
[countyNameTextField setStringValue:newRecord->countyName];
[stateAbbrTextField setStringValue:newRecord->stateAbbr];
[stateNameTextField setStringValue:newRecord->stateName];
[nationAbbrTextField setStringValue:newRecord->nationAbbr];
[nationNameTextField setStringValue:newRecord->nationName];
[areaCodeTextField setStringValue:newRecord->areaCode];
[elevationTextField setStringValue:newRecord->elevation];
[featureCodeTextField setStringValue:newRecord->featureCode];
[featureNameTextField setStringValue:newRecord->featureName];
[latitudeTextField setStringValue:newRecord->latitude];
[longitudeNameTextField setStringValue:newRecord->longitude];
[censusTextField setStringValue:newRecord->census];
[remarkTextField setStringValue:newRecord->remark];
[timeZoneTextField setStringValue:newRecord->timeZone];
[[zipScrollView docView] setText:newRecord->zip];
return self;
}
// Clear all of the text fields.
- clearTextFields
{
[countyCodeTextField setStringValue:""];
[countyNameTextField setStringValue:""];
[stateAbbrTextField setStringValue:""];
[stateNameTextField setStringValue:""];
[nationAbbrTextField setStringValue:""];
[nationNameTextField setStringValue:""];
[areaCodeTextField setStringValue:""];
[elevationTextField setStringValue:""];
[featureCodeTextField setStringValue:""];
[featureNameTextField setStringValue:""];
[latitudeTextField setStringValue:""];
[longitudeNameTextField setStringValue:""];
[censusTextField setStringValue:""];
[remarkTextField setStringValue:""];
[timeZoneTextField setStringValue:""];
[[zipScrollView docView] setText:""];
return self;
}
// Remove the entries from the pull down lists.
- resetPullDownList
{
int statesCount, state;
statesCount = [statesPullDownList count];
for(state = statesCount-1; state > 0; state--)
[statesPullDownList removeItemAt:state];
statesCount = [moreStatesPullDownList count];
for(state = statesCount-1; state > 0; state--)
[moreStatesPullDownList removeItemAt:state];
return self;
}
- textDidChange:textObject
{
TextField *textField;
// Clear the text fields.
[self clearTextFields];
// Reset the pull down list.
[self resetPullDownList];
[statesPullDownListButton setEnabled:NO];
[moreStatesPullDownListButton setEnabled:NO];
// Emtpy all city records.
[records empty];
textField = [textObject delegate];
if(textField == cityTextField)
[stateTextField setStringValue:""];
return self;
}
- appWillTerminate:sender
{
[subprocess send:"bye"];
[subprocess terminate:self];
return self;
}
// Process the output from the subprocess object.
- subprocessOutput:(char *)buffer
{
static char *message;
char *period, *start, *end;
static BOOL firstTime = YES;
static BOOL login = YES;
int cityCount;
MenuCell *menuCell;
// Malloc a message buffer only once.
if(firstTime)
{
message = malloc(10000);
firstTime = NO;
}
// Process a login.
strcat(message, buffer);
if(login)
{
period = rindex(message, '.');
if(period )
if(*(period - 1) == '\n')
{
[loginMessageWindow close];
[NXApp stopModal];
[queryButton setEnabled:YES];
bzero(message, strlen(message)+1);
*(message+1) = '\0';
login = NO;
}
}
// Process the data returned from the database server.
else
{
// Process the records only after all buffers have arrived.
if(strstr(message, "\n."))
{
// No cities found.
if(!strstr(message, "\n0"))
{
bzero(message, strlen(message)+1);
[searchingMessageWindow close];
[queryButton setEnabled:YES];
}
// Process the cities.
else
{
// Process all records but the last one (states).
cityCount = 0;
start = strstr(message, "\n0")+1;
while(end = strstr(start, "\n0"))
{
*end = '\0';
// Clear out the record and then stuff all of the information into it.
bzero(&record, sizeof(struct dataRecord));
if(stuffer(&record, start))
{
if(cityCount < 39)
{
// Check for duplicate states. The data base has some duplicates.
while( [statesPullDownList indexOfItem:record.stateName] != -1)
strcat(record.stateName, " ");
// Get the state and add a button to the pulldown list.
menuCell = [statesPullDownList addItem:record.stateName];
[menuCell setTag:cityCount];
}
else
{
// Check for duplicate states. The data base has some duplicates.
while( [moreStatesPullDownList indexOfItem:record.stateName] != -1)
strcat(record.stateName, " ");
// Get the state and add a button to the pulldown list.
menuCell = [moreStatesPullDownList addItem:record.stateName];
[menuCell setTag:cityCount];
}
[records addElement:&record];
cityCount++;
}
start = end+1;
}
// Now process the last record (state).
end = strstr(start, "\n.");
*end = '\0';
// Clear out the record and then stuff all of the information into it.
bzero(&record, sizeof(struct dataRecord));
if(stuffer(&record, start))
{
if(cityCount < 39)
{
// Check for duplicate states. The data base has some duplicates.
while( [statesPullDownList indexOfItem:record.stateName] != -1)
strcat(record.stateName, " ");
// Get the state and add a button to the pulldown list.
menuCell = [statesPullDownList addItem:record.stateName];
[menuCell setTag:cityCount];
}
else
{
// Check for duplicate states. The data base has some duplicates.
while( [moreStatesPullDownList indexOfItem:record.stateName] != -1)
strcat(record.stateName, " ");
// Get the state and add a button to the pulldown list.
menuCell = [moreStatesPullDownList addItem:record.stateName];
[menuCell setTag:cityCount];
}
[records addElement:&record];
cityCount++;
}
if(cityCount > 0)
[statesPullDownListButton setEnabled:YES];
else
[statesPullDownListButton setEnabled:NO];
if(cityCount > 38)
[moreStatesPullDownListButton setEnabled:YES];
else
[moreStatesPullDownListButton setEnabled:NO];
bzero(message, strlen(message)+1);
[searchingMessageWindow close];
[queryButton setEnabled:YES];
}
}
}
return self;
}
// This is sent when the connection is broken, or when we quit the application.
- subprocessDone
{
NXRunAlertPanel("BYE", "You are no longer connected to the remote machine.",
NULL, NULL, NULL);
return self;
}
// Display any error messages received.
- subprocessError:(const char *)errorString
{
[errorMessageWindow makeKeyAndOrderFront:self];
[[errorMessageScrollView docView] setText:errorString];
return self;
}
// Takes each string, corresponding to a city and specific state, and puts the information
// into a record.
int stuffer(struct dataRecord *record , char *entry)
{
char *zero, *start, *copy, *linefeed, *string, *nextSpace;
BOOL stateFound = NO, done = NO;
int firstChar, blank;
copy = malloc(strlen(entry) + 1);
strcpy(copy, entry);
// Find the city field and put it into the structure.
zero = index(copy, '0')+2;
linefeed = index(zero, '\n');
*(linefeed) = '\0';
strcpy(record->city, zero);
// Get the fields and put them into the structure.
start = linefeed +1;
while(!done)
{
// Put a null terminator at the end of each line before processing the line.
linefeed = index(start, '\n');
*linefeed = '\0';
// Based on the first character, put the field into the structure.
firstChar = (int)*(start);
switch(firstChar)
{
case '1':
// Get the county code.
string = index(start, ' ')+1;
nextSpace = index(string, ' ');
*nextSpace = '\0';
strcpy(record->countyCode, string);
// Get the county name.
string = nextSpace+1;
strcpy(record->countyName, string);
break;
case '2':
stateFound = YES;
// Get the state abbreviation.
string = index(start, ' ')+1;
nextSpace = index(string, ' ');
*nextSpace = '\0';
strcpy(record->stateAbbr, string);
// Get the state name.
string = nextSpace+1;
strcpy(record->stateName, string);
break;
case '3':
// Get the nation abbreviation.
string = index(start, ' ')+1;
nextSpace = index(string, ' ');
*nextSpace = '\0';
strcpy(record->nationAbbr, string);
// Get the nation name.
string = nextSpace+1;
strcpy(record->nationName, string);
break;
case 'A':
// Get the area code.
string = index(start, ' ')+1;
strcpy(record->areaCode, string);
break;
case 'E':
// Get the elevation.
string = index(start, ' ')+1;
strcpy(record->elevation, string);
break;
case 'F':
// Get the feature code.
string = index(start, ' ')+1;
nextSpace = index(string, ' ');
*nextSpace = '\0';
strcpy(record->featureCode, string);
// Get the feature name.
string = nextSpace+1;
strcpy(record->featureName, string);
break;
case 'L':
// Get the latitude.
string = index(start, ' ')+1;
nextSpace = index(string, ' ');
for(blank = 0; blank < 3; blank++)
nextSpace = index(nextSpace+1, ' ');
*nextSpace = '\0';
strcpy(record->latitude, string);
// Get the longitude.
string = nextSpace+1;
strcpy(record->longitude, string);
break;
case 'P':
// Get the census.
string = index(start, ' ')+1;
strcpy(record->census, string);
break;
case 'R':
// Get the remark.
string = index(start, ' ')+1;
strcpy(record->remark, string);
break;
case 'T':
// Get the time zone.
string = index(start, ' ')+1;
strcpy(record->timeZone, string);
break;
case 'Z':
// Get the zip codes.
string = index(start, ' ')+1;
strcat(record->zip, string);
break;
}
if(index(linefeed+1, '\n'))
start = linefeed + 1;
else
done = YES;
}
free(copy);
return stateFound;
}
@end